﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Google.GData.Calendar;
using Google.GData.Client;
using Google.GData.Extensions;
using Journal.Persistence;

namespace Journal.Helpers
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {

        private CalendarPersistenceManager calendarPersistenceManager = new CalendarPersistenceManager();
        private EventPersistenceManager eventPersistenceManager = new EventPersistenceManager();
        private BackgroundWorker worker;
        private CalendarFeed calendars;
        private EventFeed calendarEvents;
        private Domain.Calendar selectedCalendar = null;
        private Domain.Event selectedEvent = null;

        //private int lastGoogleCalendarIndex = 0;
        enum Mode { Local, Google }
        private Mode CalendarMode
        {
            get
            {
                if (this.radioButtonGoogleCalendars.IsChecked.Value)
                {
                    return Mode.Google;
                }
                else
                {
                    return Mode.Local;
                }
            }
        }

        public MainWindow()
        {
            InitializeComponent();
            eventControlsEnabled(false, false);
            eventControlsEnabled(false, true);
            calendarControlsEnabled(false, false);
        }

        /// <summary>
        /// Fills comboBoxCalendars with data from google calendars.
        /// </summary>
        /// <param name="feed">Calendar feed retrieved by CalendarService</param>
        private void fillGoogleCalendars(CalendarFeed feed)
        {
            foreach (CalendarEntry gcal in feed.Entries)
            {
                Domain.Calendar newCalendar = new Domain.Calendar();
                newCalendar.GoogleCalendar = gcal;
                this.comboBoxCalendars.Items.Add(newCalendar);
            }
        }

        private void clearCalendarComboBox()
        {
            this.comboBoxCalendars.Items.Clear();
            this.calendars = null;
        }

        private void saveCurrentSelection()
        {
            this.selectedCalendar = this.comboBoxCalendars.SelectedItem as Domain.Calendar;
            this.selectedEvent = this.listBoxEvents.SelectedItem as Domain.Event;
        }

        private void loadCalendarSelection()
        {
            foreach (Domain.Calendar calendar in this.comboBoxCalendars.Items)
            {
                if (this.selectedCalendar != null && calendar.IsSameAs(this.selectedCalendar))
                {
                    this.comboBoxCalendars.SelectedItem = calendar;
                    return;
                }
            }

            this.comboBoxCalendars.SelectedIndex = 0;
        }

        private void loadEventSelection()
        {
            foreach (Domain.Event evnt in this.listBoxEvents.Items)
            {
                if (this.selectedEvent != null && evnt.IsSameAs(this.selectedEvent))
                {
                    this.listBoxEvents.SelectedItem = evnt;
                    return;
                }
            }
        }

        private void reloadGoogleCalendars()
        {
            saveCurrentSelection();
            clearCalendarComboBox();
            loadGoogleCalendars();
        }

        private void loadGoogleCalendars()
        {
            // If user is not logged in invoke login window
            if (GoogleServices.Instance.CalendarService.Credentials == null)
            {
                GoogleLogin loginWindow = new GoogleLogin();
                loginWindow.Owner = this; // so that window will show always on top
                this.calendars = (CalendarFeed)loginWindow.ShowLoginAndRetrieveCalendarFeed();
                finishLoadingCalendars();
            }
            // Just retrieve calendars from service
            else
            {
                worker = new BackgroundWorker();
                worker.DoWork += delegate(object sender, DoWorkEventArgs e)
                {
                    this.calendars = GoogleServices.Instance.RetrieveUserCalendars();
                };
                worker.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e)
                {
                    if (e.Error == null && !e.Cancelled)
                    {
                        finishLoadingCalendars();
                    }
                    else MessageBoxHelper.ShowExceptionFromWorkerError(e);
                    EnableControls();
                };
                // Run worker
                DisableControls();
                worker.RunWorkerAsync();
            }
        }

        private void finishLoadingCalendars()
        {
            if (this.calendars != null)
            {
                fillGoogleCalendars(this.calendars);
                // this triggers the comboBoxCalendars_SelectionChanged event which invokes changeCalendar() method
                loadCalendarSelection();
            }
            else
            {
                if (GoogleServices.Instance.CalendarService.Credentials != null)
                {
                    string text = Utilities.GetLocalizedValue("Journal", "Locale",
                        "GoogleCalendarsRetrieveFail_Message");
                    string caption = Utilities.GetLocalizedValue("Journal", "Locale",
                        "GoogleCalendarsRetrieveFail_Caption");
                    MessageBox.Show(text, caption, MessageBoxButton.OK, MessageBoxImage.Error);
                }
                radioButtonLocalCalendars.IsChecked = true;
            }
        }

        //TODO calendar highlighting
        private void fillCalendar(List<DateTime> eventDates)
        {

        }

        private void fillGoogleEventsList(EventFeed feed)
        {
            // Clear listbox first
            this.listBoxEvents.Items.Clear();
            List<DateTime> eventDates = new List<DateTime>();
            foreach (EventEntry gevent in feed.Entries)
            {
                Domain.Event newEvent = new Domain.Event();
                newEvent.GoogleEvent = gevent;
                if (this.checkBoxShowAll.IsChecked.Value)
                {
                    this.listBoxEvents.Items.Add(newEvent);
                }
                else
                {
                    DateTime calendarDate;
                    if (this.calendar.SelectedDates.Count == 0) calendarDate = this.calendar.DisplayDate;
                    else calendarDate = this.calendar.SelectedDate.Value;

                    if (gevent.Times.Count > 0 && Utilities.AreSameDaysInYear(gevent.Times[0].StartTime, calendarDate))
                    {
                        this.listBoxEvents.Items.Add(newEvent);
                    }
                }
                if (gevent.Times.Count > 0)
                    eventDates.Add(gevent.Times[0].StartTime);
            }
            // Attempt to load last selected event
            loadEventSelection();
        }

        private void loadGoogleEvents(string calendarID)
        {
            string eventsFeedURI = Utilities.BuildEventsFeedURI(calendarID);

            worker = new BackgroundWorker();
            worker.DoWork += delegate(object sender, DoWorkEventArgs e)
            {
                this.calendarEvents = GoogleServices.Instance.RetrieveCalendarEvents(eventsFeedURI,
                        GoogleServices.OrderBy.starttime.ToString(), GoogleServices.SortOrder.ascending.ToString());
            };
            worker.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e)
            {
                if (e.Error == null && !e.Cancelled)
                {
                    if (this.calendarEvents != null)
                    {
                        fillGoogleEventsList(this.calendarEvents);
                    }
                }
                else
                {
                    MessageBoxHelper.ShowExceptionFromWorkerError(e);
                    clearCalendarComboBox();
                }
                EnableControls();
            };
            // Run worker
            DisableControls();
            worker.RunWorkerAsync();
        }

        private void changeCalendar()
        {
            if (this.comboBoxCalendars.Items.Count > 0)
            {
                Domain.Calendar selectedCalendar = comboBoxCalendars.SelectedItem as Domain.Calendar;

                // Disable/enable UI controls for deleting calendar according to calendar's Primary attribute
                calendarControlsEnabled(!selectedCalendar.Primary, true);
                // Load events for selected calendar
                string calendarID = Utilities.ExtractCalendarId(selectedCalendar.GoogleCalendar.SelfUri.Content);
                loadGoogleEvents(calendarID);
            }
        }

        #region CRUD operations

        private void createCalendar()
        {
            string windowTitle = Utilities.GetLocalizedValue("Journal", "Locale", "CalendarWindow_Title_New");
            Domain.Calendar newCalendar = new Domain.Calendar();
            CalendarWindow calendarWindow = new CalendarWindow(windowTitle, newCalendar);
            calendarWindow.Owner = this;
            newCalendar = calendarWindow.ShowAndRetrieveCalendar();

            if (newCalendar != null)
            {
                if (CalendarMode == Mode.Google)
                {
                    CalendarEntry createdCalendar = Utilities.GetGoogleCalendarEntry(newCalendar, true);
                    worker = new BackgroundWorker();
                    worker.DoWork += delegate(object sender, DoWorkEventArgs e)
                    {
                        GoogleServices.Instance.CreateCalendar(createdCalendar);
                    };
                    worker.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e)
                    {
                        if (e.Error == null && !e.Cancelled) reloadGoogleCalendars();
                        else
                        {
                            MessageBoxHelper.ShowExceptionFromWorkerError(e);
                            EnableControls();
                        }
                    };
                    // Run worker
                    DisableControls();
                    worker.RunWorkerAsync();
                }
                else if (CalendarMode == Mode.Local)
                {

                }
            }
        }

        private void editCalendar(Domain.Calendar calendar)
        {
            string windowTitle = Utilities.GetLocalizedValue("Journal", "Locale", "CalendarWindow_Title_Edit");
            Domain.Calendar selectedCalendar = this.comboBoxCalendars.SelectedItem as Domain.Calendar;
            CalendarWindow calendarWindow = new CalendarWindow(windowTitle, selectedCalendar);
            calendarWindow.Owner = this;
            selectedCalendar = calendarWindow.ShowAndRetrieveCalendar();

            if (selectedCalendar != null)
            {
                if (CalendarMode == Mode.Google)
                {
                    CalendarEntry editedCalendar = Utilities.GetGoogleCalendarEntry(selectedCalendar, false);
                    worker = new BackgroundWorker();
                    worker.DoWork += delegate(object sender, DoWorkEventArgs e)
                    {
                        GoogleServices.Instance.UpdateCalendar(editedCalendar);
                    };
                    worker.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e)
                    {
                        if (e.Error == null && !e.Cancelled) reloadGoogleCalendars();
                        else
                        {
                            MessageBoxHelper.ShowExceptionFromWorkerError(e);
                            EnableControls();
                        }
                    };
                    // Run worker
                    DisableControls();
                    worker.RunWorkerAsync();
                }
                else if (CalendarMode == Mode.Local)
                {

                }
            }
        }

        private void deleteCalendar(Domain.Calendar calendar)
        {
            string text = Utilities.GetLocalizedValue("Journal", "Locale", "Calendar_Delete_Message");
            string caption = Utilities.GetLocalizedValue("Journal", "Locale", "MessageBoxWarning_Caption");

            if (MessageBox.Show(text, caption, MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes)
            {
                if (CalendarMode == Mode.Google)
                {
                    worker = new BackgroundWorker();
                    worker.DoWork += delegate(object sender, DoWorkEventArgs e)
                    {
                        GoogleServices.Instance.DeleteCalendar(calendar.GoogleCalendar);
                    };
                    worker.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e)
                    {
                        if (e.Error == null && !e.Cancelled) reloadGoogleCalendars();
                        else
                        {
                            MessageBoxHelper.ShowExceptionFromWorkerError(e);
                            EnableControls();
                        }
                    };
                    // Run worker
                    DisableControls();
                    worker.RunWorkerAsync();
                }
                else if (CalendarMode == Mode.Local)
                {
                    calendarPersistenceManager.Delete(calendar);
                }
            }
        }

        private void createEvent()
        {
            string windowTitle = Utilities.GetLocalizedValue("Journal", "Locale", "EventWindow_Title_New");
            Domain.Event newEvent = new Domain.Event();
            // Set calendar to event
            newEvent.Calendar = this.comboBoxCalendars.SelectedItem as Domain.Calendar;
            // Set StartTime, EndTime and AllDay properties
            newEvent.AllDay = false;
            DateTime start = this.calendar.SelectedDates.Count == 0 ? this.calendar.DisplayDate : this.calendar.SelectedDate.Value;
            start = start.AddHours(DateTime.Now.Hour + 1);
            DateTime end = start.AddHours(1);

            newEvent.StartTime = start;
            newEvent.EndTime = end;

            // Display event window
            EventWindow eventWindow = new EventWindow(windowTitle, newEvent, this.comboBoxCalendars.Items);
            eventWindow.Owner = this;
            newEvent = eventWindow.ShowAndRetrieveEvent();

            if (newEvent != null)
            {
                if (CalendarMode == Mode.Google)
                {
                    EventEntry eventEntry = Utilities.GetGoogleEventEntry(newEvent, true);
                    worker = new BackgroundWorker();
                    worker.DoWork += delegate(object sender, DoWorkEventArgs e)
                    {
                        string calendarUri = Utilities.BuildEventsFeedURI(
                            Utilities.ExtractCalendarId(newEvent.Calendar.GoogleCalendar.SelfUri.Content));
                        GoogleServices.Instance.CreateEvent(new Uri(calendarUri), eventEntry);
                    };
                    worker.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e)
                    {
                        if (e.Error == null && !e.Cancelled) reloadGoogleCalendars();
                        else
                        {
                            MessageBoxHelper.ShowExceptionFromWorkerError(e);
                            EnableControls();
                        }
                    };
                    // Run worker
                    DisableControls();
                    worker.RunWorkerAsync();
                }
                else if (CalendarMode == Mode.Local)
                {

                }
            }
        }

        private void editEvent(Domain.Event thisEvent)
        {
            string windowTitle = Utilities.GetLocalizedValue("Journal", "Locale", "EventWindow_Title_Edit");
            Domain.Event selectedEvent = this.listBoxEvents.SelectedItem as Domain.Event;
            selectedEvent.Calendar = this.comboBoxCalendars.SelectedItem as Domain.Calendar;
            EventWindow eventWindow = new EventWindow(windowTitle, selectedEvent, this.comboBoxCalendars.Items);
            eventWindow.Owner = this;
            Domain.Event editedEvent = eventWindow.ShowAndRetrieveEvent();

            if (editedEvent != null)
            {
                if (CalendarMode == Mode.Google)
                {
                    EventEntry eventEntry = Utilities.GetGoogleEventEntry(editedEvent, false);
                    worker = new BackgroundWorker();
                    worker.DoWork += delegate(object sender, DoWorkEventArgs e)
                    {
                        if (selectedEvent.Calendar.Equals(editedEvent.Calendar))
                        {
                            GoogleServices.Instance.UpdateEvent(eventEntry);
                        }
                        else
                        {
                            GoogleServices.Instance.DeleteEvent(eventEntry);
                            string calendarUri = Utilities.BuildEventsFeedURI(
                                Utilities.ExtractCalendarId(editedEvent.Calendar.GoogleCalendar.SelfUri.Content));
                            GoogleServices.Instance.CreateEvent(new Uri(calendarUri), eventEntry);
                        }
                    };
                    worker.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e)
                    {
                        if (e.Error == null && !e.Cancelled) reloadGoogleCalendars();
                        else
                        {
                            MessageBoxHelper.ShowExceptionFromWorkerError(e);
                            EnableControls();
                        }
                    };
                    // Run worker
                    DisableControls();
                    worker.RunWorkerAsync();
                }
                else if (CalendarMode == Mode.Local)
                {

                }
            }
        }

        private void deleteEvents(IList events)
        {
            string text = events.Count == 1 ? Utilities.GetLocalizedValue("Journal", "Locale", "Event_DeleteSingle_Message")
                : Utilities.GetLocalizedValue("Journal", "Locale", "Event_DeleteMultiple_Message");
            string caption = Utilities.GetLocalizedValue("Journal", "Locale", "MessageBoxWarning_Caption");

            if (MessageBox.Show(text, caption, MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes)
            {
                if (CalendarMode == Mode.Google)
                {
                    worker = new BackgroundWorker();
                    worker.DoWork += delegate(object sender, DoWorkEventArgs e)
                    {
                        foreach (Domain.Event evnt in events)
                        {
                            GoogleServices.Instance.DeleteEvent(evnt.GoogleEvent);
                        }
                    };
                    worker.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e)
                    {
                        if (e.Error == null && !e.Cancelled) reloadGoogleCalendars();
                        else
                        {
                            MessageBoxHelper.ShowExceptionFromWorkerError(e);
                            EnableControls();
                        }
                    };
                    // Run worker
                    DisableControls();
                    worker.RunWorkerAsync();
                }
                else if (CalendarMode == Mode.Local)
                {

                }
            }
        }

        #endregion

        #region GUI component events declarations

        private void radioButtonLocalCalendars_Checked(object sender, RoutedEventArgs e)
        {

        }

        private void radioButtonGoogleCalendars_Checked(object sender, RoutedEventArgs e)
        {
            reloadGoogleCalendars();
        }

        private void buttonNewCalendar_Click(object sender, RoutedEventArgs e)
        {
            createCalendar();
        }

        private void buttonEditCalendar_Click(object sender, RoutedEventArgs e)
        {
            editCalendar(this.comboBoxCalendars.SelectedItem as Domain.Calendar);
        }

        private void buttonDeleteCalendar_Click(object sender, RoutedEventArgs e)
        {
            deleteCalendar(this.comboBoxCalendars.SelectedItem as Domain.Calendar);
        }

        private void calendar_SelectedDatesChanged(object sender, SelectionChangedEventArgs e)
        {
            fillGoogleEventsList(this.calendarEvents);
        }

        private void calendar_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            createEvent();
        }

        private void checkBoxShowAll_Checked(object sender, RoutedEventArgs e)
        {
            saveCurrentSelection();
            fillGoogleEventsList(this.calendarEvents);
        }

        private void checkBoxShowAll_Unchecked(object sender, RoutedEventArgs e)
        {
            saveCurrentSelection();
            fillGoogleEventsList(this.calendarEvents);
        }

        private void comboBoxCalendars_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (this.comboBoxCalendars.SelectedIndex == -1)
            {
                calendarControlsEnabled(false, false);
                eventControlsEnabled(false, true);
            }
            else
            {
                calendarControlsEnabled(true, false);
                eventControlsEnabled(true, true);
            }
            changeCalendar();
        }

        private void listBoxEvents_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            ListBox listBox = sender as ListBox;

            switch (listBox.SelectedItems.Count)
            {
                case 0:
                    eventControlsEnabled(false, false);
                    this.groupBoxEventDetail.DataContext = null;
                    break;
                case 1:
                    eventControlsEnabled(true, false);
                    this.groupBoxEventDetail.DataContext = listBox.SelectedItem;
                    break;
                default:
                    eventControlsEnabled(true, false);
                    this.groupBoxEventDetail.DataContext = null;
                    break;
            }
        }

        private void menuAbout_Click(object sender, RoutedEventArgs e)
        {
            AboutWindow aboutWindow = new AboutWindow();
            aboutWindow.Owner = this;
            aboutWindow.ShowDialog();
        }

        private void menuExit_Click(object sender, RoutedEventArgs e)
        {
            JournalApp.Current.Shutdown();
        }

        private void menuCalendarAdd_Click(object sender, RoutedEventArgs e)
        {
            createCalendar();
        }

        private void menuCalendarEdit_Click(object sender, RoutedEventArgs e)
        {
            editCalendar(this.comboBoxCalendars.SelectedItem as Domain.Calendar);
        }

        private void menuCalendarDelete_Click(object sender, RoutedEventArgs e)
        {
            deleteCalendar(this.comboBoxCalendars.SelectedItem as Domain.Calendar);
        }

        private void menuEventAdd_Click(object sender, RoutedEventArgs e)
        {
            createEvent();
        }

        private void menuEventEdit_Click(object sender, RoutedEventArgs e)
        {
            editEvent(this.listBoxEvents.SelectedItem as Domain.Event);
        }

        private void menuEventDelete_Click(object sender, RoutedEventArgs e)
        {
            deleteEvents(this.listBoxEvents.SelectedItems);
        }

        private void buttonAddEvent_Click(object sender, RoutedEventArgs e)
        {
            createEvent();
        }

        private void buttonEditEvent_Click(object sender, RoutedEventArgs e)
        {
            editEvent(this.listBoxEvents.SelectedItem as Domain.Event);
        }

        private void buttonDeleteEvent_Click(object sender, RoutedEventArgs e)
        {
            deleteEvents(this.listBoxEvents.SelectedItems);
        }

        private void buttonRefresh_Click(object sender, RoutedEventArgs e)
        {
            if (CalendarMode == Mode.Google) reloadGoogleCalendars();
        }

        private void listBoxEvents_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            editEvent(this.listBoxEvents.SelectedItem as Domain.Event);
        }

        #endregion

        #region Small helper methods

        private void eventControlsEnabled(bool enabled, bool allEvents)
        {
            if (allEvents)
            {
                this.menuEvents.IsEnabled = enabled;
                this.toolBarEvents.IsEnabled = enabled;
            }
            else
            {
                this.menuEventEdit.IsEnabled = enabled;
                this.menuEventDelete.IsEnabled = enabled;
                this.buttonEditEvent.IsEnabled = enabled;
                this.buttonDeleteEvent.IsEnabled = enabled;
            }
        }

        private void calendarControlsEnabled(bool enabled, bool justDelete)
        {
            this.menuCalendarDelete.IsEnabled = enabled;
            this.buttonDeleteCalendar.IsEnabled = enabled;

            if (!justDelete)
            {
                this.menuCalendarEdit.IsEnabled = enabled;
                this.buttonEditCalendar.IsEnabled = enabled;
                this.calendar.IsEnabled = enabled;
                this.listBoxEvents.IsEnabled = enabled;
                this.comboBoxCalendars.IsEnabled = enabled;
                this.stackPanelEventControls.IsEnabled = enabled;
                this.buttonRefresh.IsEnabled = enabled;
            }
        }

        private void DisableControls()
        {
            // Disable controls
            this.menu.IsEnabled = false;
            this.tabControl.IsEnabled = false;
            // Change status bar items and start progressbar
            this.progressBar.IsEnabled = true;
            this.progressBar.IsIndeterminate = true;
            this.statusBarItemIcon.Visibility = System.Windows.Visibility.Hidden;
            string status = Utilities.GetLocalizedValue("Journal", "Locale", "StatusBar_Loading");
            this.statusBarItemStatus.Content = status;
        }

        private void EnableControls()
        {
            // Enable controls
            this.menu.IsEnabled = true;
            this.tabControl.IsEnabled = true;
            // Change status bar items and stop progressbar
            this.progressBar.IsEnabled = false;
            this.progressBar.IsIndeterminate = false;
            this.statusBarItemIcon.Visibility = System.Windows.Visibility.Visible;
            string status = Utilities.GetLocalizedValue("Journal", "Locale", "StatusBar_Ready");
            this.statusBarItemStatus.Content = status;
        }

        #endregion

    }
}
